package com.wei.czz.framework.game.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.game.GameDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.game.GameEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.game.GamePo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.tool.PageQuery;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.framework.common.handler.redis.RedisHashHandler;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.game.GameFormVo;
import com.wei.czz.common.vo.game.GameVo;
import com.wei.czz.framework.admin.service.UserService;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.game.service.GamePrizeFlowService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.wei.czz.framework.game.dao.GameDao;
import com.wei.czz.framework.game.entity.GameEntity;
import com.wei.czz.framework.game.service.GameService;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2023-03-16 11:35:49
 * className: GameServiceImpl 游戏请求服务接口实现类
 * version: 1.0
 * description:
 */
@Service("gameService")
@AllArgsConstructor
public class GameServiceImpl extends ServiceImpl<GameDao, GameEntity> implements GameService {

    private static final Logger log = LoggerFactory.getLogger(GameServiceImpl.class);

    private final UserService userService;

    private final GamePrizeFlowService gamePrizeFlowService;

    private final RedisHashHandler redisHashHandler;

    private final RedissonClient redissonClient;

    private final AsyncService asyncService;

    @Override
    public void saveGame(GameVo gameVo) {
        // 获取正在保存游戏的用户主键
        Long optUserId = SecurityUtils.getUserId();
        Date date = new Date();

        GameEntity game = new GameEntity();
        // 数据填充
        BeanUtils.copyProperties(gameVo, game);
        game.setId(null)
            .setDefaultPrizeId(CommonEnum.ZERO.getLongValue())
            .setPrizePoolFlag(GameEnum.NO_PRIZE_POOL.getValue())
            .setStatus(CommonEnum.ONE.getValue())
            .setDeleteFlag(CommonEnum.ZERO.getValue())
            .setUpdateTime(date)
            .setUpdateUser(optUserId)
            .setCreateTime(date)
            .setCreateUser(optUserId);
        // 插入数据
        baseMapper.insert(game);

        log.info("游戏数据插入完成");

        GamePo gamePo = new GamePo();
        // 数据填充
        BeanUtils.copyProperties(game, gamePo);

        // 添加缓存
        redisHashHandler.put(RedisConstant.GAME_MAP, String.valueOf(game.getId()), gamePo);
    }

    @Override
    public PageDto<GameDto> getGamePageList(GameFormVo gameFormVo) {

        Page<GameEntity> page = PageQuery.initPage(gameFormVo);

        LambdaQueryWrapper<GameEntity> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        gameLambdaQueryWrapper.select(GameEntity::getId, GameEntity::getGameName, GameEntity::getStartTime,
                GameEntity::getEndTime, GameEntity::getStatus, GameEntity::getCreateTime, GameEntity::getCreateUser,
                GameEntity::getRemark);
        // 匹配条件
        gameLambdaQueryWrapper.like(StringUtils.isNotEmpty(gameFormVo.getWord()), GameEntity::getGameName, gameFormVo.getWord())
                .ge(Objects.nonNull(gameFormVo.getStartTime()), GameEntity::getStartTime, gameFormVo.getStartTime())
                .le(Objects.nonNull(gameFormVo.getEndTime()), GameEntity::getEndTime, gameFormVo.getEndTime())
                .eq(Objects.nonNull(gameFormVo.getStatus()), GameEntity::getStatus, gameFormVo.getStatus())
                .eq(GameEntity::getDeleteFlag, CommonEnum.ZERO.getValue());
        // 查询数据
        baseMapper.selectPage(page, gameLambdaQueryWrapper);

        List<GameEntity> gameList = page.getRecords();
        if (gameList.isEmpty()) {
            log.info("分页查询游戏结果为空");
            return new PageDto<>(Collections.emptyList(), page.getTotal(), gameFormVo.getPage(), gameFormVo.getLimit());
        }

        Set<Long> userIdSet = gameList.stream().map(GameEntity::getCreateUser).collect(Collectors.toSet());
        Map<Long, String> userNameMap = userService.getNameMap(userIdSet, Function.identity());

        List<GameDto> gameDtoList = gameList.stream()
                .map(game -> {
                    GameDto gameDto = new GameDto();
                    // 属性值复制
                    BeanUtils.copyProperties(game, gameDto);
                    gameDto.setId(game.getId().toString())
                            .setCreateUser(userNameMap.get(game.getCreateUser()));
                    return gameDto;
                })
                .collect(Collectors.toList());
        // 数据封装返回
        return new PageDto<>(gameDtoList, page.getTotal(), gameFormVo.getPage(), gameFormVo.getLimit());
    }

    @Override
    public GamePo getGamePo(Long id) {

        String _id = id.toString();
        // 查询缓存
        GamePo gamePo = redisHashHandler.get(RedisConstant.GAME_MAP, _id);
        if (gamePo == null) {
            RLock lock = redissonClient.getLock(RedisConstant.LOCK + RedisConstant.GAME_MAP + _id);
            // 加锁
            lock.lock();
            try {
                // 重复查询缓存
                gamePo = redisHashHandler.get(RedisConstant.GAME_MAP, _id);
                if (gamePo != null) {
                    return gamePo;
                }

                // 查询数据库
                LambdaQueryWrapper<GameEntity> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
                // 查询字段
                gameLambdaQueryWrapper.select(GameEntity::getStartTime, GameEntity::getEndTime, GameEntity::getDefaultPrizeId,
                        GameEntity::getStatus);
                // 条件匹配
                gameLambdaQueryWrapper.eq(GameEntity::getId, id);
                // 查询数据
                List<GameEntity> gameList = baseMapper.selectList(gameLambdaQueryWrapper);
                if (gameList.isEmpty()) {
                    log.warn("游戏数据不存在。id={}", id);
                    throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏数据不存在，请确认。");
                }
                GameEntity game = gameList.get(0);

                gamePo = new GamePo();
                // 数据填充
                gamePo.setStartTime(game.getStartTime())
                        .setEndTime(game.getEndTime())
                        .setDefaultPrizeId(game.getDefaultPrizeId())
                        .setStatus(game.getStatus());

                // 添加缓存
                redisHashHandler.put(RedisConstant.GAME_MAP, _id, gamePo);
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    // 解锁
                    lock.unlock();
                }
            }
        }
        return gamePo;
    }

    @Override
    public void updateGame(GameVo gameVo) {
        // 获取正在修改游戏的用户主键
        Long optUserId = SecurityUtils.getUserId();
        Date date = new Date();

        GameEntity game = new GameEntity();
        game.setId(gameVo.getId())
            .setStartTime(gameVo.getStartTime())
            .setEndTime(gameVo.getEndTime())
            .setRemark(gameVo.getRemark())
            .setUpdateTime(date)
            .setUpdateUser(optUserId);
        // 更新数据
        int count = baseMapper.updateById(game);
        log.info("游戏数据更新完成。count={}", count);
        if (count == 0) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏数据不存在，请确认");
        }

        String id = gameVo.getId().toString();

        // 删除缓存
        this.deleteGameCache(id);
    }

    @Override
    public void updateGameStatus(UpdateStatusVo updateStatusVo) {

        if (CommonEnum.ZERO.getValue().equals(updateStatusVo.getStatus())) {
            // 判断是否存在没有初始化游戏奖品池/未设置默认奖品的游戏
            LambdaQueryWrapper<GameEntity> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 查询字段
            gameLambdaQueryWrapper.select(GameEntity::getDefaultPrizeId, GameEntity::getPrizePoolFlag);
            // 匹配条件
            gameLambdaQueryWrapper.in(GameEntity::getId, updateStatusVo.getIdList());
            // 查询数据
            List<GameEntity> gameList = baseMapper.selectList(gameLambdaQueryWrapper);
            if (gameList.isEmpty()) {
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "游戏数据不存在，请确认。");
            }
            boolean bool = gameList.size() == 1;
            for (GameEntity game : gameList) {
                if (CommonEnum.ZERO.getLongValue().equals(game.getDefaultPrizeId())) {
                    String message;
                    if (bool) {
                        message = "请先设置默认奖品，然后再启动游戏";
                    } else {
                        message = "选中游戏中包含未设置默认奖品的数据，启动失败";
                    }
                    throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), message);
                }
            }
            for (GameEntity game : gameList) {
                if (GameEnum.NO_PRIZE_POOL.getValue().equals(game.getPrizePoolFlag())) {
                    String message;
                    if (bool) {
                        message = "请先初始化奖品池，然后再启动游戏";
                    } else {
                        message = "选中游戏中包含未初始化奖品池的数据，启动失败";
                    }
                    throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), message);
                }
            }
        }

        // 获取正在修改游戏状态的用户主键
        Long optUserId = SecurityUtils.getUserId();
        Date date = new Date();

        LambdaUpdateWrapper<GameEntity> gameLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 更新字段
        gameLambdaUpdateWrapper.set(GameEntity::getStatus, updateStatusVo.getStatus())
                .set(GameEntity::getUpdateTime, date)
                .set(GameEntity::getUpdateUser, optUserId);
        // 匹配条件
        gameLambdaUpdateWrapper.in(GameEntity::getId, updateStatusVo.getIdList())
                .eq(GameEntity::getDeleteFlag, CommonEnum.ZERO.getValue());

        // 更新数据
        int count = baseMapper.update(null, gameLambdaUpdateWrapper);
        log.info("游戏状态更新完成。count={}", count);
        if (count == 0) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "游戏状态修改失败，请确认");
        }

        String[] ids = updateStatusVo.getIdList().stream().map(Object::toString).toArray(String[]::new);
        // 删除缓存
        this.deleteGameCache(ids);
    }

    @Override
    public void updatePrizePoolFlag(Long id, Integer prizePoolFlag, Long userId) {
        LambdaUpdateWrapper<GameEntity> gameLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 更新字段
        gameLambdaUpdateWrapper.set(GameEntity::getPrizePoolFlag, prizePoolFlag)
                .set(GameEntity::getUpdateTime, new Date())
                .set(GameEntity::getUpdateUser, userId);
        // 匹配条件
        gameLambdaUpdateWrapper.eq(GameEntity::getId, id)
                .eq(GameEntity::getDeleteFlag, CommonEnum.ZERO.getValue());
        // 更新数据
        int count = baseMapper.update(null, gameLambdaUpdateWrapper);
        if (count == 0) {
            log.error("游戏是否初始化奖品池字段更新失败。count={}", count);
        } else {
            log.info("游戏是否初始化奖品池字段更新完成。count={}", count);
        }
    }

    @Override
    public void updateDefaultPrize(Long id, Long prizeId, Long oldPrizeId, Long optUserId) {
        LambdaUpdateWrapper<GameEntity> gameLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 更新字段
        gameLambdaUpdateWrapper.set(GameEntity::getDefaultPrizeId, prizeId)
                .set(GameEntity::getUpdateTime, new Date())
                .set(GameEntity::getUpdateUser, optUserId);
        // 匹配条件
        gameLambdaUpdateWrapper.eq(GameEntity::getId, id)
                .eq(GameEntity::getDeleteFlag, CommonEnum.ZERO.getValue());
        // 更新数据
        int count = baseMapper.update(null, gameLambdaUpdateWrapper);
        log.info("游戏默认奖品更新完成。count={}", count);

        // 操作记录
        asyncService.execute(() -> {
            if (!CommonEnum.isDefaultId(oldPrizeId)) {
                // 切换默认奖品
                gamePrizeFlowService.saveGamePrizeFlow(oldPrizeId, "取消默认奖品", "", optUserId);
            }
            if (!CommonEnum.isDefaultId(prizeId)) {
                // 记录奖品流水
                gamePrizeFlowService.saveGamePrizeFlow(prizeId, "设为默认奖品","", optUserId);
            }
        });

        // 删除缓存
        this.deleteGameCache(id.toString());
    }

    @Override
    public void deleteGame(List<Long> idList) {
        // 获取正在删除游戏的用户主键
        Long optUserId = SecurityUtils.getUserId();
        Date date = new Date();

        LambdaUpdateWrapper<GameEntity> gameLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 更新字段
        gameLambdaUpdateWrapper.set(GameEntity::getDeleteFlag, CommonEnum.ONE.getValue())
                .set(GameEntity::getUpdateTime, date)
                .set(GameEntity::getUpdateUser, optUserId);
        // 匹配条件
        gameLambdaUpdateWrapper.in(GameEntity::getId, idList)
                .eq(GameEntity::getDeleteFlag, CommonEnum.ZERO.getValue());
        // 更新数据
        int count = baseMapper.update(null, gameLambdaUpdateWrapper);
        log.info("游戏数据删除完成。count={}", count);
        if (count == 0) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "游戏删除失败，请确认");
        }

        String[] ids = idList.stream().map(Object::toString).toArray(String[]::new);
        // 删除缓存
        this.deleteGameCache(ids);
    }

    /**
     * 删除游戏缓存数据操作
     * @param ids 主键数组
     */
    public void deleteGameCache(String... ids) {
        // 删除缓存
        redisHashHandler.delete(RedisConstant.GAME_MAP, ids);
        asyncService.schedule(() -> {
            // 延时双删
            redisHashHandler.delete(RedisConstant.GAME_MAP, ids);
        }, 1500, TimeUnit.MILLISECONDS);
    }
}